const puppeteer = require('puppeteer-core');
const path = require('path');
const { chromePath, redisConf, wxpusher } = require('./ti_config');
const fs = require('fs');
const { Console } = require('console');
const charset = require('superagent-charset');
const superagent =  charset(require('superagent'));
require('superagent-proxy')(superagent);
const redis = require('redis');

const sleep = ms => {
    return new Promise(resolve => setTimeout(resolve, ms));
}

const redisClient =  redis.createClient(redisConf.port, redisConf.host);
redisClient.auth(redisConf.password);

//获取机器mac地址
const os = require("os");
const networkInterfaces = os.networkInterfaces();
let macAddr;
for (const name in networkInterfaces) {
    if (name.indexOf('lo') > -1) continue;
    for (const v of networkInterfaces[name]) {
        if (v.family != 'IPv4' || v.mac == '00:00:00:00:00:00') continue;
        macAddr = v.mac;
        break;
    }
}

//防止消息轰炸，做一个简单过滤
// const noticeMap = {};
// const avoidNoticeFreq = (orderable_number) => {
//     if (!noticeMap[orderable_number]) {
//         noticeMap[orderable_number] = [];
//     }
//     if (noticeMap[orderable_number].length < 3) {
//         noticeMap[orderable_number].push(new Date().getTime());
//         return true;
//     }
//     const now = new Date().getTime();
//     for (const ts of noticeMap[orderable_number]) {
//         if (now - ts > 3600000) {
//             noticeMap[orderable_number] = [];
//             return true;
//         }
//     }
//     return false;
// }
const avoidNoticeFreq = async (serial) => {
    const key = 'ti_freq_' + serial;
    const num = await get(key);
    if (num && num >= 1) {//调整成60分钟只提醒1次
        return false;
    }
    await incr(key);
    await expire(key, 3600);
    return true;
}
exports.avoidNoticeFreq = avoidNoticeFreq;

const avoidNoticeFreqCookie = async (serial) => {
    const key = 'ti_freq_cookie_' + serial;
    const num = await get(key);
    if (num && num >= 1) {//调整成60分钟只提醒1次
        return false;
    }
    await incr(key);
    await expire(key, 3600);
    return true;
}
exports.avoidNoticeFreqCookie = avoidNoticeFreqCookie;

const avoidNoticeFreqByKey = async (key) => {
    const num = await get(key);
    if (num && num >= 1) {//调整成60分钟只提醒1次
        return false;
    }
    await incr(key);
    await expire(key, 3600);
    return true;
}
exports.avoidNoticeFreqByKey = avoidNoticeFreqByKey;




const easyPushWechatMsg = async (msg) => {
    try {
        await httpPost('http://wxpusher.zjiecode.com/api/send/message', JSON.stringify({
            appToken: wxpusher.appToken,
            content: msg,
            contentType: 1,
            uids: wxpusher.uids,
        }), 'application/json');
    } catch(e) {
        logger(e);
    }
}
exports.easyPushWechatMsg = easyPushWechatMsg;

const easyPushWechatMsgAdmin = async (msg) => {
    try {
        await httpPost('http://wxpusher.zjiecode.com/api/send/message', JSON.stringify({
            appToken: wxpusher.appToken,
            content: msg,
            contentType: 1,
            uids: wxpusher.admin_uids,
        }), 'application/json');
    } catch(e) {
        logger(e);
    }
}
exports.easyPushWechatMsgAdmin = easyPushWechatMsgAdmin;

const tiPost = async (url, data, referer) => {
    const cookie = await get('ti_worker_cookie');
    const arr = cookie.split(';');

    let chromeVer = 95;
    let ua;
    let platform = '"Windows"';

    arr.map(str => {
        str = str.trim();
        const key = str.substr(0, str.indexOf('='));
        const value = decodeURIComponent(str.substr(str.indexOf('=')+1));
        if (key == 'ti_ua') {//获取ua
            ua = value;
            try {
                chromeVer = ua.match(/Chrome\/(\d+)/)[1];
            } catch(e){}

            if (ua.indexOf('Mac') > -1) {
                platform = '"macOS"';
            }
        }
    });

	return new Promise((resolve, reject) => {
		superagent.post(url).timeout(30000)
			.set('Host', 'www.ti.com.cn')
			.set('Connection', 'keep-alive')
			.set('sec-ch-ua', `"Google Chrome";v="${chromeVer}", "Chromium";v="${chromeVer}", ";Not A Brand";v="99"`)
			.set('sec-ch-ua-mobile', '?0')
			.set('user-agent', ua)
			.set('Content-Type', 'application/json')
			.set('X-Sec-Clge-Req-Type', 'ajax')
			.set('Expires', '0')
			.set('Cache-Control', 'no-store, must-revalidate')
			.set('sec-ch-ua-platform', platform)
			.set('Accept', '*/*')
			.set('Origin', 'https://www.ti.com.cn')
			.set('Sec-Fetch-Site', 'same-origin')
			.set('Sec-Fetch-Mode', 'cors')
			.set('Sec-Fetch-Dest', 'empty')
			.set('Referer', referer)
			.set('Accept-Encoding', 'gzip, deflate, br')
			.set('Accept-Language', 'zh-CN,zh;q=0.9,en;q=0.8')
			.set('Cookie', cookie)
			.send(data)
			.end((err, res) => {
				if (err) {
					return reject(err);
				}
				resolve(res.text);
			});
	});
}
exports.tiPost = tiPost;

const get = (key) => {
    return new Promise((resolve, reject) => {
        redisClient.get(key, (err, data) => {
            if (err) {
                reject(err);
            } else {
                resolve(data);
            }
        });
    });
}
exports.get = get;
const lpush = (key, value) => {
    return new Promise((resolve, reject) => {
        redisClient.lpush(key, value, (err, data) => {
            if (err) {
                reject(err);
            } else {
                resolve(data);
            }
        });
    });
}
exports.lpush = lpush;
const lpop = (key) => {
    return new Promise((resolve, reject) => {
        redisClient.lpop(key, (err, data) => {
            if (err) {
                reject(err);
            } else {
                resolve(data);
            }
        });
    });
}
exports.lpop = lpop;
const set = (key, value) => {
    return new Promise((resolve, reject) => {
        redisClient.set(key, value, (err, data) => {
            if (err) {
                reject(err);
            } else {
                resolve(data);
            }
        });
    });
}
exports.set = set;
const expire = (key, second) => {
    return new Promise((resolve, reject) => {
        redisClient.expire(key, second, (err, data) => {
            if (err) {
                reject(err);
            } else {
                resolve(data);
            }
        });
    });
}
exports.expire = expire;
const hgetall = (key) => {
    return new Promise((resolve, reject) => {
        redisClient.hgetall(key, (err, data) => {
            if (err) {
                reject(err);
            } else {
                resolve(data);
            }
        });
    });
}
exports.hgetall = hgetall;

const lrange = (key, start, end) => {
    return new Promise((resolve, reject) => {
        redisClient.lrange(key, start, end, (err, data) => {
            if (err) {
                reject(err);
            } else {
                resolve(data);
            }
        });
    });
}
exports.lrange = lrange;

const hlen = (key) => {
    return new Promise((resolve, reject) => {
        redisClient.hlen(key, (err, data) => {
            if (err) {
                reject(err);
            } else {
                resolve(data);
            }
        });
    });
}
exports.hlen = hlen;

const hsetnx = (key, field, value) => {
    return new Promise((resolve, reject) => {
        redisClient.hsetnx(key, field, value, (err, data) => {
            if (err) {
                reject(err);
            } else {
                resolve(data);
            }
        });
    });
}
const hset = (key, field, value) => {
    return new Promise((resolve, reject) => {
        redisClient.hset(key, field, value, (err, data) => {
            if (err) {
                reject(err);
            } else {
                resolve(data);
            }
        });
    });
}
exports.hset = hset;
const hget = (key, field) => {
    return new Promise((resolve, reject) => {
        redisClient.hget(key, field, (err, data) => {
            if (err) {
                reject(err);
            } else {
                resolve(data);
            }
        });
    });
}
exports.hget = hget;
const incr = (key) => {
    return new Promise((resolve, reject) => {
        redisClient.incr(key, (err, data) => {
            if (err) {
                reject(err);
            } else {
                resolve(data);
            }
        });
    });
}
const setnx = (key, value) => {
    return new Promise((resolve, reject) => {
        redisClient.setnx(key, value, (err, data) => {
            if (err) {
                reject(err);
            } else {
                resolve(data);
            }
        });
    });
}
const del = (key) => {
    return new Promise((resolve, reject) => {
        redisClient.del(key, (err, data) => {
            if (err) {
                reject(err);
            } else {
                resolve(data);
            }
        });
    });
}
exports.del = del;

const hdel = (key, field) => {
    return new Promise((resolve, reject) => {
        redisClient.hdel(key, field, (err, data) => {
            if (err) {
                reject(err);
            } else {
                resolve(data);
            }
        });
    });
}

const runKey = 'ti_worker_run';
const statusKey = 'ti_worker_status';

const updateErrorTimes = async (timeoutCount) => {
    await hset(statusKey, macAddr, JSON.stringify({
        num: timeoutCount,
        last: new Date().getTime(),
    }));
}
exports.updateErrorTimes = updateErrorTimes;

const waitOtherWorker = async () => {
    while (1) {
        try {
            const setRunData = JSON.stringify({
                id: macAddr, last: new Date().getTime(),
            });
    
            //检查是否有机器在运行
            const ret = await get(runKey);
            if (ret) {
                // logger('A1');
                const retJson = JSON.parse(ret);
                if (retJson.id == macAddr) {//是自己在运行，更新运行时间
                    // logger('A2');
                    await set(runKey, setRunData);
                    return;
                }
                // logger('A3');
                //如果超过2分钟都没有更新，说明进程已经挂了
                if ((new Date().getTime()) - retJson.last > 120000) {
                    // logger('A4');
                    await del(runKey);
                    await hdel(statusKey, retJson.id);
                }
            } else {
                // logger('B1');
                //自己不在则加进去
                await hsetnx(statusKey, macAddr, JSON.stringify({
                    num: 0,
                    last: new Date().getTime(),
                }));
                
                const maps = await hgetall(statusKey);
                let isCircleEnd = 1;
                for (const id in maps) {
                    const data = JSON.parse(maps[id]);
                    if (data.num < 3) {
                        isCircleEnd = 0;
                    }
    
                    if (id == macAddr && data.num < 3) {
                        // logger('B2');
                        const ret = await setnx(runKey, setRunData);
                        if (ret) {//抢锁成功，启动
                            return;
                        }
                    }
    
                    // logger('B3');
                    if ((new Date().getTime()) - data.last > 120000) {
                        await hdel(statusKey, id);
                    }
                }
    
                //所有机器错误次数都达到了3次，清空
                if (isCircleEnd) {
                    await del(statusKey);
                }
            }
        } catch(e) {
            logger(e);
        }

        await sleep(2000);
    }
}
exports.waitOtherWorker = waitOtherWorker;

//删除正在运行的key
const closeRun = async () => {
    await del(runKey);
}
exports.closeRun = closeRun;

const randBetween = (min, max) => {
    if (min > max) {
        logger('错误输入');
        return 0;
    }
    const diff = max - min;
    return min + Math.ceil(Math.random() * 10000) % (diff + 1);
}
exports.randBetween = randBetween;

let browser;
const closeBrowser = async () => {
    await browser.close();
}
exports.closeBrowser = closeBrowser;

const initPage = async (proxy = null) => {
    //每次启动浏览器，大小有一定随机
    const width = randBetween(1500, 1750);
    const height = randBetween(900, 1100);
    const args = [
        '--window-size='+width+','+height,
    ];
    if (proxy) {
        logger('使用代理', proxy);
        args.push('--proxy-server='+proxy);
    }

    browser = await puppeteer.launch({
        headless: false,
        executablePath: path.resolve(chromePath),
        ignoreHTTPSErrors : true,   // 忽略证书错误
        ignoreDefaultArgs:['--enable-automation'],  // 去掉左上角 Chrome 正受自动软件控制
        //devtools: true,//打开控制台
        defaultViewport: null,//自动适应大小
        args,
    });

    const page = await browser.newPage();
    global.page = page;

    // await page.evaluateOnNewDocument(() => {
    //          Object.defineProperty(navigator, 'webdriver', {
    //          get: () => undefined,
    //     });
    //  })

    //伪装，防止被判断是爬虫
    if (process.platform == 'win32') {
        await page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36');
    }
    await page.evaluateOnNewDocument(() => {
        delete navigator.__proto__.webdriver;
    });
    await page.evaluateOnNewDocument(() => {
        const mockedFns = []
        const fakeData = {
            mimeTypes: [
                {
                    type: 'application/pdf',
                    suffixes: 'pdf',
                    description: 'Portable Document Format',
                },
                {
                    type: 'text/pdf',
                    suffixes: 'pdf',
                    description: 'Portable Document Format',
                },
            ],
            plugins: [
                {
                    name: 'PDF Viewer',
                    filename: 'internal-pdf-viewer',
                    description: 'Portable Document Format'
                },
                {
                    name: 'Chrome PDF Viewer',
                    filename: 'internal-pdf-viewer',
                    description: 'Portable Document Format'
                },
                {
                    name: 'Chromium PDF Viewer',
                    filename: 'internal-pdf-viewer',
                    description: 'Portable Document Format'
                },
                {
                    name: 'Microsoft Edge PDF Viewer',
                    filename: 'internal-pdf-viewer',
                    description: 'Portable Document Format'
                },
                {
                    name: 'WebKit built-in PDF',
                    filename: 'internal-pdf-viewer',
                    description: 'Portable Document Format'
                },
            ],
            fns: {
                namedItem: instanceName => {
                    // Returns the Plugin/MimeType with the specified name.
                    const fn = function(name) {
                        return this[name] || null
                    }
                    mockedFns.push({
                        ref: fn,
                        name: 'namedItem'
                    })
                    return fn
                },
                item: instanceName => {
                    // Returns the Plugin/MimeType at the specified index into the array.
                    const fn = function(index) {
                        return this[index] || null
                    }
                    mockedFns.push({
                        ref: fn,
                        name: 'item'
                    })
                    return fn
                },
                refresh: instanceName => {
                    // Refreshes all plugins on the current page, optionally reloading documents.
                    const fn = function() {
                        return undefined
                    }
                    mockedFns.push({
                        ref: fn,
                        name: 'refresh'
                    })
                    return fn
                }
            }
        };

        const getSubset = (keys, obj) =>
            keys.reduce((a, c) => ({
                ...a,
                [c]: obj[c]
            }), {})

        function generatePluginArray() {
            const arr = fakeData.plugins
                .map(obj => getSubset(['name', 'filename', 'description'], obj))
                .map(obj => {
                    const mimes = fakeData.mimeTypes
                    // Add mimetypes
                    mimes.forEach((mime, index) => {
                        navigator.mimeTypes[mime.type].enabledPlugin = obj
                        obj[mime.type] = navigator.mimeTypes[mime.type]
                        obj[index] = navigator.mimeTypes[mime.type]
                    })
                    obj.length = mimes.length
                    return obj
                })
                .map(obj => {
                    // Mock functions
                    obj.namedItem = fakeData.fns.namedItem('Plugin')
                    obj.item = fakeData.fns.item('Plugin')
                    return obj
                })
                .map(obj => Object.setPrototypeOf(obj, Plugin.prototype))
            arr.forEach(obj => {
                arr[obj.name] = obj
            })
            // Mock functions
            arr.namedItem = fakeData.fns.namedItem('PluginArray')
            arr.item = fakeData.fns.item('PluginArray')
            arr.refresh = fakeData.fns.refresh('PluginArray')
            return Object.setPrototypeOf(arr, PluginArray.prototype)
        }
        const pluginArray = generatePluginArray();
        Object.defineProperty(navigator, 'plugins', {
            get: () => pluginArray
        });
    });
    await page.evaluateOnNewDocument(() => {
        const getParameterProxyHandler = {
            get(target, key) {
                try {
                    // Mitigate Chromium bug (#130)
                    if (typeof target[key] === 'function') {
                        return target[key].bind(target)
                    }
                    return Reflect.get(target, key)
                } catch (err) {
                    throw err
                }
            },
            apply: function(target, thisArg, args) {
                const param = (args || [])[0]
                // UNMASKED_VENDOR_WEBGL
                if (param === 37445) {
                    return 'ATI Technologies Inc.'
                }
                // UNMASKED_RENDERER_WEBGL
                if (param === 37446) {
                    return 'AMD Radeon Pro 5300M OpenGL Engine'
                }
                try {
                    return Reflect.apply(target, thisArg, args)
                } catch (err) {
                    throw err
                }
            }
        }
        const proxy = new Proxy(
            WebGLRenderingContext.prototype.getParameter,
            getParameterProxyHandler
        )
        // To find out the original values here: Object.getOwnPropertyDescriptors(WebGLRenderingContext.prototype.getParameter)
        Object.defineProperty(WebGLRenderingContext.prototype, 'getParameter', {
            configurable: true,
            enumerable: false,
            writable: false,
            value: proxy
        })
    });
    await page.evaluateOnNewDocument(() => {
        window.chrome.runtime = {
            "OnInstalledReason":{"CHROME_UPDATE":"chrome_update","INSTALL":"install","SHARED_MODULE_UPDATE":"shared_module_update","UPDATE":"update"},
            "OnRestartRequiredReason":{"APP_UPDATE":"app_update","OS_UPDATE":"os_update","PERIODIC":"periodic"},
            "PlatformArch":{"ARM":"arm","ARM64":"arm64","MIPS":"mips","MIPS64":"mips64","X86_32":"x86-32","X86_64":"x86-64"},
            "PlatformNaclArch":{"ARM":"arm","MIPS":"mips","MIPS64":"mips64","X86_32":"x86-32","X86_64":"x86-64"},
            "PlatformOs":{"ANDROID":"android","CROS":"cros","LINUX":"linux","MAC":"mac","OPENBSD":"openbsd","WIN":"win"},
            "RequestUpdateCheckStatus":{"NO_UPDATE":"no_update","THROTTLED":"throttled","UPDATE_AVAILABLE":"update_available"},
            "id":function(){},
            "sendMessage":function(){},
            "connect":function(){},
        };
    });

    return page;
}
exports.initPage = initPage;

async function getIp(){    
    logger(223);
    let result = '';
    //const result = await httpPost('http://webapi.http.zhimacangku.com/getip?num=1&type=2&pro=&city=0&yys=0&port=2&pack=193720&ts=0&ys=0&cs=0&lb=1&sb=0&pb=4&mr=1&regions=');
    //const result = await httpPost('http://ecs.hailiangip.com:8422/api/getIp?type=2&num=1&pid=-1&unbindTime=600&cid=-1&orderId=O21111814053891278876&time=1637215756&sign=a0073f9d5076c00af21f8824e238ecdc&noDuplicate=0&dataType=0&lineSeparator=0&singleIp=');
    try{
        result = await httpPost('http://tiqu.pyhttp.taolop.com/getip?count=1&neek=15825&type=2&yys=0&port=1&sb=&mr=1&sep=0&ts=1&pack=9444');
    }catch(e){
        logger(111);
        //easyPushWechatMsg('ip代理出了问题');
    }
    logger(result);
    let ip_info = JSON.parse(result);

    if(ip_info['code']!=0){
        return false;
    }
    let ip_result = ip_info['data'][0];


    
    return ip_result['ip']+':'+ip_result['port'];
}

const initPage2 = async (proxy = null) => {
    //每次启动浏览器，大小有一定随机
    const width = randBetween(1500, 1750);
    const height = randBetween(900, 1100);
    const args = [
        '--window-size='+width+','+height,
    ];
    logger(22);
    proxy = await getIp();
    if(proxy == false){
        await sleep(3000);
        proxy = await getIp();
    }
    if(proxy == false){
        await sleep(3000);
        proxy = await getIp();
    }
    //proxy = '123.245.250.181:44930';
    if (proxy) {
        logger('使用代理', proxy);
        args.push('--proxy-server='+proxy);
    }

    browser = await puppeteer.launch({
        headless: false,
        executablePath: path.resolve(chromePath),
        ignoreHTTPSErrors : true,   // 忽略证书错误
        ignoreDefaultArgs:['--enable-automation'],  // 去掉左上角 Chrome 正受自动软件控制
        //devtools: true,//打开控制台
        defaultViewport: null,//自动适应大小
        args,
    });

    const page = await browser.newPage();
    global.page = page;

    //伪装，防止被判断是爬虫
    if (process.platform == 'win32') {
        await page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36');
    }
    await page.evaluateOnNewDocument(() => {
        delete navigator.__proto__.webdriver;
    });
    await page.evaluateOnNewDocument(() => {
        const mockedFns = []
        const fakeData = {
            mimeTypes: [
                {
                    type: 'application/pdf',
                    suffixes: 'pdf',
                    description: 'Portable Document Format',
                },
                {
                    type: 'text/pdf',
                    suffixes: 'pdf',
                    description: 'Portable Document Format',
                },
            ],
            plugins: [
                {
                    name: 'PDF Viewer',
                    filename: 'internal-pdf-viewer',
                    description: 'Portable Document Format'
                },
                {
                    name: 'Chrome PDF Viewer',
                    filename: 'internal-pdf-viewer',
                    description: 'Portable Document Format'
                },
                {
                    name: 'Chromium PDF Viewer',
                    filename: 'internal-pdf-viewer',
                    description: 'Portable Document Format'
                },
                {
                    name: 'Microsoft Edge PDF Viewer',
                    filename: 'internal-pdf-viewer',
                    description: 'Portable Document Format'
                },
                {
                    name: 'WebKit built-in PDF',
                    filename: 'internal-pdf-viewer',
                    description: 'Portable Document Format'
                },
            ],
            fns: {
                namedItem: instanceName => {
                    // Returns the Plugin/MimeType with the specified name.
                    const fn = function(name) {
                        return this[name] || null
                    }
                    mockedFns.push({
                        ref: fn,
                        name: 'namedItem'
                    })
                    return fn
                },
                item: instanceName => {
                    // Returns the Plugin/MimeType at the specified index into the array.
                    const fn = function(index) {
                        return this[index] || null
                    }
                    mockedFns.push({
                        ref: fn,
                        name: 'item'
                    })
                    return fn
                },
                refresh: instanceName => {
                    // Refreshes all plugins on the current page, optionally reloading documents.
                    const fn = function() {
                        return undefined
                    }
                    mockedFns.push({
                        ref: fn,
                        name: 'refresh'
                    })
                    return fn
                }
            }
        };

        const getSubset = (keys, obj) =>
            keys.reduce((a, c) => ({
                ...a,
                [c]: obj[c]
            }), {})

        function generatePluginArray() {
            const arr = fakeData.plugins
                .map(obj => getSubset(['name', 'filename', 'description'], obj))
                .map(obj => {
                    const mimes = fakeData.mimeTypes
                    // Add mimetypes
                    mimes.forEach((mime, index) => {
                        navigator.mimeTypes[mime.type].enabledPlugin = obj
                        obj[mime.type] = navigator.mimeTypes[mime.type]
                        obj[index] = navigator.mimeTypes[mime.type]
                    })
                    obj.length = mimes.length
                    return obj
                })
                .map(obj => {
                    // Mock functions
                    obj.namedItem = fakeData.fns.namedItem('Plugin')
                    obj.item = fakeData.fns.item('Plugin')
                    return obj
                })
                .map(obj => Object.setPrototypeOf(obj, Plugin.prototype))
            arr.forEach(obj => {
                arr[obj.name] = obj
            })
            // Mock functions
            arr.namedItem = fakeData.fns.namedItem('PluginArray')
            arr.item = fakeData.fns.item('PluginArray')
            arr.refresh = fakeData.fns.refresh('PluginArray')
            return Object.setPrototypeOf(arr, PluginArray.prototype)
        }
        const pluginArray = generatePluginArray();
        Object.defineProperty(navigator, 'plugins', {
            get: () => pluginArray
        });
    });
    await page.evaluateOnNewDocument(() => {
        const getParameterProxyHandler = {
            get(target, key) {
                try {
                    // Mitigate Chromium bug (#130)
                    if (typeof target[key] === 'function') {
                        return target[key].bind(target)
                    }
                    return Reflect.get(target, key)
                } catch (err) {
                    throw err
                }
            },
            apply: function(target, thisArg, args) {
                const param = (args || [])[0]
                // UNMASKED_VENDOR_WEBGL
                if (param === 37445) {
                    return 'ATI Technologies Inc.'
                }
                // UNMASKED_RENDERER_WEBGL
                if (param === 37446) {
                    return 'AMD Radeon Pro 5300M OpenGL Engine'
                }
                try {
                    return Reflect.apply(target, thisArg, args)
                } catch (err) {
                    throw err
                }
            }
        }
        const proxy = new Proxy(
            WebGLRenderingContext.prototype.getParameter,
            getParameterProxyHandler
        )
        // To find out the original values here: Object.getOwnPropertyDescriptors(WebGLRenderingContext.prototype.getParameter)
        Object.defineProperty(WebGLRenderingContext.prototype, 'getParameter', {
            configurable: true,
            enumerable: false,
            writable: false,
            value: proxy
        })
    });
    await page.evaluateOnNewDocument(() => {
        window.chrome.runtime = {
            "OnInstalledReason":{"CHROME_UPDATE":"chrome_update","INSTALL":"install","SHARED_MODULE_UPDATE":"shared_module_update","UPDATE":"update"},
            "OnRestartRequiredReason":{"APP_UPDATE":"app_update","OS_UPDATE":"os_update","PERIODIC":"periodic"},
            "PlatformArch":{"ARM":"arm","ARM64":"arm64","MIPS":"mips","MIPS64":"mips64","X86_32":"x86-32","X86_64":"x86-64"},
            "PlatformNaclArch":{"ARM":"arm","MIPS":"mips","MIPS64":"mips64","X86_32":"x86-32","X86_64":"x86-64"},
            "PlatformOs":{"ANDROID":"android","CROS":"cros","LINUX":"linux","MAC":"mac","OPENBSD":"openbsd","WIN":"win"},
            "RequestUpdateCheckStatus":{"NO_UPDATE":"no_update","THROTTLED":"throttled","UPDATE_AVAILABLE":"update_available"},
            "id":function(){},
            "sendMessage":function(){},
            "connect":function(){},
        };
    });

    return page;
}
exports.initPage2 = initPage2;


exports.sleep = sleep;

//console信息输出到文件
const consoleLogger = new Console({
	stdout: fs.createWriteStream('./stdout.log', {
		flags:'a+'
	}),
	stderr: fs.createWriteStream('./stdout.log', {
		flags:'a+'
	}),
});

const logger = function() {
	console.log('[', (new Date()).Format("yyyy-MM-dd hh:mm:ss"), ']', ...arguments);
	consoleLogger.log('[', (new Date()).Format("yyyy-MM-dd hh:mm:ss"), ']', ...arguments);
	return null;
}
exports.logger = logger;



const httpPost = (url, data, contentType = null) => {
	return new Promise((resolve, reject) => {
		superagent.post(url).timeout(20000)
			.set('user-agent','Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36')
			.set('content-type',contentType)
			.send(data)
			.end((err, res) => {
				if (err) {
					return reject(err);
				}
				resolve(res.text);
			});
	});
}
exports.httpPost = httpPost;

const httpGet = (url, referer) => {
	return new Promise((resolve, reject) => {
		superagent.get(url).timeout(20000)
			.set('user-agent','Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36')
			.set('Referer', referer)
			.end((err, res) => {
				if (err) {
					return reject(err);
				}
				resolve(res.text);
			});
	});
}
exports.httpGet = httpGet;

// 对Date的扩展，将 Date 转化为指定格式的String
// 月(M)、日(d)、小时(h)、分(m)、秒(s)、季度(q) 可以用 1-2 个占位符，
// 年(y)可以用 1-4 个占位符，毫秒(S)只能用 1 个占位符(是 1-3 位的数字)
// 例子：
// (new Date()).Format("yyyy-MM-dd hh:mm:ss.S") ==> 2006-07-02 08:09:04.423
// (new Date()).Format("yyyy-M-d h:m:s.S")      ==> 2006-7-2 8:9:4.18
Date.prototype.Format = function (fmt) { //author: meizz
	var o = {
		"M+": this.getMonth() + 1,                 //月份
		"d+": this.getDate(),                    //日
		"h+": this.getHours(),                   //小时
		"m+": this.getMinutes(),                 //分
		"s+": this.getSeconds(),                 //秒
		"q+": Math.floor((this.getMonth() + 3) / 3), //季度
		"S": this.getMilliseconds()             //毫秒
	};
	if (/(y+)/.test(fmt))
		fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
	for (var k in o)
		if (new RegExp("(" + k + ")").test(fmt))
			fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
	return fmt;
}

